今天是最後一篇了,我們接著完成我們最後的新增功能吧!
其實新增功能與先前的部分沒有差太多,一樣我們先回到 todoApi 去處理新增的功能,如下:
// src/features/api/todoApi.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
// 做一份與 server 相同的 Todo 格式提供 component 使用
export interface Todo {
id: number;
text: string;
active: boolean;
done: boolean;
}
// 這裡 createApi 會自動將 endpoints 的 name 去組成相對應的 query
// 以 getAll 為例,使用時會組成 useGetAllQuery 的 function
export const todoApi = createApi({
reducerPath: 'todoApi',
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:8080/'}),
tagTypes: ['Todos'],
endpoints: (builder) => ({
// 取全部資料
getAll: builder.query<Todo[], void>({
// 這裡的 query 代表接續 base 要傳入的值,providesTags 就是記錄一下對應的 tagType 詳細 https://redux-toolkit.js.org/rtk-query/usage/automated-refetchin
query: () => `todos`,
providesTags: [{type: 'Todos', id: 'LIST'}]
}),
// 修改 todo
// 這裡採用 mutation 意思是會拿取先前的 todo 然後回傳一個新的 todo 這段的概念與 React query 是相同的。
updateTodo: builder.mutation<Todo, Todo>({
// 這裡就是帶入參數的做法
query(todo) {
return {
url: `todos/${todo.id}`,
method: 'PUT',
body: todo,
};
},
// invalidatesTag 會自動去更新相對的 tagType 詳細一樣 https://redux-toolkit.js.org/rtk-query/usage/automated-refetchin
invalidatesTags: [{type: 'Todos', id: 'LIST'}]
}),
// 刪除功能
deleteTodo: builder.mutation<Todo, Todo>({
query(todo) {
return {
url: `todos/${todo.id}`,
method: 'DELETE',
body: todo,
};
},
invalidatesTags: [{type: 'Todos', id: 'LIST'}]
}),
// 新增功能
addTodo: builder.mutation<string, string>({
query(text) {
return {
url: `todos`,
method: 'POST',
body: {
text,
}
};
},
invalidatesTags: [{type: 'Todos', id: 'LIST'}]
})
})
})
再來,我們接著調整畫面讓新增的段落顯示出來:
// src/components/TodosList.tsx
import { useCallback, useRef } from "react";
import { Todo, todoApi } from "../features/api/todoApi"
const TodosList = () => {
// 使用剛才的 getAll query
const { data } = todoApi.useGetAllQuery();
// 這裡使用剛才的 updateTodo
const [ updateTodo ] = todoApi.useUpdateTodoMutation();
// 這裡使用剛才的 deleteTodo
const [ deleteTodo ] = todoApi.useDeleteTodoMutation();
// 這裡使用剛才的 addTodo
const [ addTodo ] = todoApi.useAddTodoMutation();
// 和之前一樣處理 input
const addTextRef = useRef<HTMLInputElement>(null);
// 這裡的話就是一般 react 的常規操作
const onToggle = useCallback((todo: Todo) => {
updateTodo({...todo, done: !todo.done})
}, [updateTodo])
// 與上述相同再操作一次
const onDelete = useCallback((todo: Todo) => {
deleteTodo(todo)
}, [deleteTodo])
const onAdd = useCallback(() => {
addTodo(addTextRef.current!.value ?? '');
addTextRef.current!.value = '';
}, [addTodo])
return (
<div className="card" style={{margin: '1rem 0'}}>
{data?.map((todo) => (
<div className="f-b-c" key={todo.id}>
<div>
<input type="checkbox" checked={todo.done} onChange={() => onToggle(todo)}/>
<span>{todo.text}</span>
</div>
<button onClick={() => onDelete(todo)}>delete</button>
</div>
))}
<div>
<input type="text" ref={addTextRef} />
<button onClick={onAdd}>add</button>
</div>
</div>
)
}
export default TodosList
測試的時候不要忘記確認 server 是有在運行的喔!
這次的內容我相信是很充足的了,從最基本的 redux → redux-toolkit → react-redux → with Typescript → RTK query ,基本上都是能直接應用在工作上的情境,也是我使用他的一些經驗總結,一直以來我也只在工作時會使用這樣的工具,當然很多新的工具也是有露出頭啦,但以實務經驗來說,它確實是首選,而且也存在好長一段時間了,可以兼容的版本也是好幾代的呢!
之後也會分享一些我所使用過有趣的狀態管理工具,但說真的,要能寫30篇的內容不參水分真的有點難,這次的紀錄我會放在我的 gitHub 上面供大家參閱,裏面的 5 個資料夾分別紀錄了這 30 天的技術精華,我有盡量站在初學者的角度去撰寫,但各位如果還是有疑問的可以再用關鍵字 google 或是留言告知,我盡快回覆,那麼今年的參賽歷程就到這邊了,感謝各位一路相挺,明年再見!!
source code: gitHub連結
redux真的水很深,感謝分享,也恭喜三十天挑戰成功~~
謝謝,更多細節其實在我的 gitHub 連結會比較好看懂。